package com.ruoyi.inspection.controller;

import cn.hutool.core.lang.UUID;
import cn.hutool.json.JSONUtil;
import com.ruoyi.common.constant.GlobalConstants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.YnStatusEnum;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.inspection.config.GovernTaskConfig;
import com.ruoyi.inspection.domain.receiveVo.*;
import com.ruoyi.system.domain.*;
import com.ruoyi.system.mapper.*;
import com.ruoyi.system.service.*;
import com.ruoyi.system.services.EnclosureServices;
import com.ruoyi.system.services.PersonPositionServices;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * http://localhost:38080/govern/person/innser
 * http://localhost:38080/govern/person/outer
 * http://localhost:38080/govern/person/enclosure
 * http://localhost:38080/govern/person/position
 *
 */

@Slf4j
@RestController
@RequestMapping("/govern/person")
public class GovernPersonPositionPush {
    @Autowired
    private TemporaryStaffAccountService temporaryStaffAccountService;
    @Autowired
    private EnclosureServices enclosureServices;
    @Autowired
    private IEnclosureInfoService enclosureInfoService;
    @Autowired
    private PersonPositionServices personPositionServices;
    @Autowired
    private KeyPersonnelAdminService keyPersonnelAdminService;
    @Resource
    private PushInfoMapper pushInfoMapper;
    @Autowired
    private SysUserMapper userMapper;
    @Autowired
    private RedisCache redisCache;


    @GetMapping("/inner")
    public AjaxResult pushInner() {
        log.error("推送内部人员基础信息 到市局，开启。。");
        pushEmployee();

        return AjaxResult.success();
    }

    @GetMapping("/outer")
    public AjaxResult callOuter() {
        pushOutEmployee();

        return AjaxResult.success();
    }

    @GetMapping("/enclosure")
    public AjaxResult callEnclosure() {
        pushEnclosure();

        return AjaxResult.success();
    }

    @GetMapping("/position")
    public AjaxResult callPosition() {
        pushEmployeePosition();

        return AjaxResult.success();
    }



    /*******************内部用户 * 每间隔6小时推送一次 *内部用户 ****************/
    @Scheduled(cron = "0 0 22 * * ?")
    public void employScheduleTask() {
        pushEmployee();
    }

    /*******************外部用户 * 每间隔6小时推送一次 *外部用户 ****************/
    @Scheduled(cron = "0 10 22 * * ?")
    public void outEmployScheduleTask() {
        pushOutEmployee();
    }

    /*******************电子围栏数据 * 一天推一次 * 电子围栏数据 ***************/
    @Scheduled(cron = "0 20 22 * * ?")
    public void enclosureScheduleTask() {
        pushEnclosure();
    }

    /*******************人员位置信息 * 每间隔7分钟推送一次 * 人员位置信息 ***************/
    @Scheduled(cron = "0 0/7 * * * ?")
    public void employeePositionScheduleTask() {
        pushEmployeePosition();
    }



    /**
     * 推送内部人员基础信息 到市局
     */
    public void pushEmployee() {
        List<KeyPersonnelAdmin> personnelAdminList = personPositionServices.synPersonnel();
        if (StringUtils.isEmpty(personnelAdminList)) {
            log.info("推送市局端 : 没有需要推送的内部人员信息！！！");
            return ;
        }

        List<ReceiveEmployee> resultList = null;
        for (KeyPersonnelAdmin person : personnelAdminList) {
            ReceiveEmployee vo =ReceiveEmployee.keyPersonnelAdminToReceiveEmployee(person);
            if (vo==null) {
                continue;
            }
            if (resultList==null) {
                resultList = new ArrayList<>();
            }

            SysUser user = userMapper.selectUserByUserName(person.getName());
            vo.setGender(user!=null && StringUtils.isNotEmpty(user.getSex()) ? user.getSex() : "0");
            person.setGovernId(vo.getId());
            resultList.add(vo);

            if (resultList.size()==5) {
                // 市局推送人员信息
                String url = GovernTaskConfig.receiveEmployeeList;
                String s = GovernTaskConfig.pushData(JSONUtil.toJsonPrettyStr(resultList), url);
                if ("请求失败".equals(s)) {
                    log.error("推送内部人员基础信息 到市局 失败！！！");
                    return ;
                }
                pushInfoMapper.insert(s);
                resultList.clear();
            }
        }

        if (StringUtils.isNotEmpty(resultList)) {
            // 市局推送人员信息
            String url = GovernTaskConfig.receiveEmployeeList;
            String s = GovernTaskConfig.pushData(JSONUtil.toJsonPrettyStr(resultList), url);
            if ("请求失败".equals(s)) {
                log.error("推送内部人员基础信息 到市局 失败！！！");
                return ;
            }
            pushInfoMapper.insert(s);
        }

        personnelAdminList.forEach(person -> {
            KeyPersonnelAdmin updateEntity = new KeyPersonnelAdmin();
            updateEntity.setGovernId(person.getGovernId());
            updateEntity.setPositionTagId(person.getPositionTagId());
            updateEntity.setPositionPhone(person.getPositionPhone());
            updateEntity.setPositionJobNumber(person.getPositionJobNumber());
            updateEntity.setPositionUserId(person.getPositionUserId());
            updateEntity.setId(person.getId());
            updateEntity.setIsUpload(1);
            keyPersonnelAdminService.update(updateEntity);
        });
    }


    /**
     * 推送外部人员基础信息 到市局
     */
    public void pushOutEmployee() {
        TemporaryStaffAccount temporaryStaffAccount = new TemporaryStaffAccount();
        temporaryStaffAccount.setYn(YnStatusEnum.Y.getCode());
        temporaryStaffAccount.setIsUpload(0);
        List<TemporaryStaffAccount> tempPersonList = temporaryStaffAccountService.selectList(temporaryStaffAccount);
        if (StringUtils.isEmpty(tempPersonList)) {
            log.info("推送市局端 : 没有需要推送的外部人员信息！！！");
            return ;
        }

        List<ReceiveEmployee> resultList = null;
        for (TemporaryStaffAccount person : tempPersonList) {
            ReceiveEmployee vo = ReceiveEmployee.temporaryStaffAccountToReceiveEmployee(person);
            if (vo==null) {
                continue;
            }
            if (resultList==null) {
                resultList = new ArrayList<>();
            }

            resultList.add(vo);
            if (resultList.size()==5) {
                // 市局推送人员信息
                String url = GovernTaskConfig.receiveEmployeeList;
                String s = GovernTaskConfig.pushData(JSONUtil.toJsonPrettyStr(resultList), url);
                if ("请求失败".equals(s)) {
                    log.error("推送外部人员基础信息 到市局 失败！！！");
                    return ;
                }
                pushInfoMapper.insert(s);
                resultList.clear();
            }
        }

        if (StringUtils.isNotEmpty(resultList)) {
            // 市局推送人员信息
            String url = GovernTaskConfig.receiveEmployeeList;
            String s = GovernTaskConfig.pushData(JSONUtil.toJsonPrettyStr(resultList), url);
            if ("请求失败".equals(s)) {
                log.error("推送外部人员基础信息 到市局 失败！！！");
                return ;
            }
            pushInfoMapper.insert(s);
        }

        tempPersonList.forEach(person -> {
            TemporaryStaffAccount updateEntity = new TemporaryStaffAccount();
            updateEntity.setGovernId(person.getGovernId());
            updateEntity.setId(person.getId());
            updateEntity.setIsUpload(1);
            temporaryStaffAccountService.update(updateEntity);
        });
    }


    /**
     * 推送电子围栏(区域边界)信息
     */
    public void pushEnclosure() {
        List<EnclosureInfo> enclosureList = enclosureServices.getEnclosureList();
        if (StringUtils.isEmpty(enclosureList)) {
            log.info("推送市局端 : 没有需要推送的围栏信息！！！");
            return ;
        }
        log.info("推送市局端 : 共查询到 {} 条围栏信息，准备推送", enclosureList.size());

        // 转换为接收对象列表
        List<ReceiveEnclosure> resultList = enclosureList.stream()
                .map(ReceiveEnclosure::enclosureInfoToReceiveEnclosure)
                .filter(Objects::nonNull)  // 过滤转换失败的对象
                .collect(Collectors.toList());
        if (StringUtils.isEmpty(resultList)) {
            log.info("推送市局端 : 没有需要推送的围栏信息（转换后为空）！！！");
            return ;
        }
        log.info("推送市局端 : 围栏信息转换完成，共 {} 条有效数据", resultList.size());

        // 批量推送围栏信息
        if (batchPushEnclosure(resultList)) {
            updateEnclosureStatus(enclosureList);
        }
    }

    // 新增：批量推送围栏信息辅助方法
    private boolean batchPushEnclosure(List<ReceiveEnclosure> enclosureList) {
        String url = GovernTaskConfig.receiveAreaList;
        String jsonData = JSONUtil.toJsonPrettyStr(enclosureList);
        String pushResult = GovernTaskConfig.pushData(jsonData, url);
        pushInfoMapper.insert(pushResult);

        if ("请求失败".equals(pushResult)) {
            log.error("推送围栏信息失败（条数：{}），数据：{}", enclosureList.size(), jsonData);
            return false;
        }
        log.info("推送围栏信息完成（条数：{}）", enclosureList.size());
        return true;
    }

    // 新增：更新围栏状态辅助方法
    private void updateEnclosureStatus(List<EnclosureInfo> enclosureList) {
        enclosureList.forEach(enclosure -> {
            EnclosureInfo updateEntity = new EnclosureInfo();
            updateEntity.setId(enclosure.getId());
            updateEntity.setGovernId(enclosure.getGovernId());
            updateEntity.setDelFlag("0");
            updateEntity.setState(enclosure.getState());
            updateEntity.setName(enclosure.getName());
            updateEntity.setAreaIds(enclosure.getAreaIds());
            updateEntity.setShape(enclosure.getShape());
            updateEntity.setEnclosureType(enclosure.getEnclosureType());
            updateEntity.setCoordinates(enclosure.getCoordinates());
            try {
                enclosureInfoService.updateEnclosureInfo(updateEntity);
            } catch (Exception e) {
                log.error("更新围栏状态失败，围栏ID：{}", enclosure.getId(), e);
            }
        });
    }



    /**
     * 推送人员定位信息
     */
    public void pushEmployeePosition() {
        // 查询内部人员列表
        KeyPersonnelAdmin queryParam = new KeyPersonnelAdmin();
        queryParam.setYn(YnStatusEnum.Y.getCode());
        queryParam.setIsUpload(1);
        List<KeyPersonnelAdmin> personnelAdminList = keyPersonnelAdminService.selectList(queryParam);
        personnelAdminList = Optional.ofNullable(personnelAdminList).orElse(Collections.emptyList());

        if (personnelAdminList.isEmpty()) {
            log.info("推送市局端：没有符合条件的内部人员（已上传且启用），无需推送定位信息");
            return;
        }
        log.info("推送市局端：共查询到 {} 名需推送定位信息的内部人员", personnelAdminList.size());

        // 使用Stream收集所有人员位置数据（替代forEach循环，更简洁）
        List<ReceiveEmployeePosition> resultList = personnelAdminList.stream()
                .flatMap(person -> {
                    // 修复点：接收单个位置对象，转换为单元素列表（null时返回空列表）
                    ReceiveEmployeePosition position = personPosition(person.getName(), person.getGovernId());
                    return Optional.ofNullable(position)
                            .map(Collections::singletonList) // 单个对象转为列表
                            .orElse(Collections.emptyList())  // null时返回空列表
                            .stream();
                })
                .collect(Collectors.toList());

        if (resultList.isEmpty()) {
            log.info("推送市局端：所有人员均无有效定位数据，无需推送");
            return;
        }
        log.info("推送市局端：共收集到 {} 条人员定位数据，准备批量推送", resultList.size());


        // 批量推送（每5条一批，修复原代码使用resultList的bug）
        List<ReceiveEmployeePosition> pushList = new ArrayList<>(5); // 初始化容量为5，减少扩容
        int totalPushed = 0;

        for (ReceiveEmployeePosition position : resultList) {
            pushList.add(position);

            // 达到5条或遍历结束时推送
            if (pushList.size() == 5 || resultList.indexOf(position) == resultList.size() - 1) {
                boolean pushSuccess = pushPositionBatch(pushList);
                if (pushSuccess) {
                    totalPushed += pushList.size();
                    pushList.clear();
                } else {
                    log.error("推送市局端：当前批次推送失败，暂停推送（批次数据：{}）", JSONUtil.toJsonStr(pushList));
                    return; // 可根据业务需求改为"继续推送下一批"
                }
            }
        }

        log.info("推送市局端：人员定位数据推送完成，共成功推送 {} 条", totalPushed);
    }

    // 新增：批量推送辅助方法
    private boolean pushPositionBatch(List<ReceiveEmployeePosition> batch) {
        String url = GovernTaskConfig.receiveEmployeePositionList;
        try {
            String jsonData = JSONUtil.toJsonPrettyStr(batch);
            String pushResult = GovernTaskConfig.pushData(jsonData, url);

            if ("请求失败".equals(pushResult)) {
                log.error("推送市局端：批次推送失败（{}条数据）", batch.size());
                return false;
            }
            pushInfoMapper.insert(pushResult);
            log.info("推送市局端：批次推送成功（{}条数据）", batch.size());
            return true;
        } catch (Exception e) {
            log.error("推送市局端：批次推送异常（数据：{}）", JSONUtil.toJsonStr(batch), e);
            return false;
        }
    }

    /**
     * 查询人员位置信息
     * @param userName 人员姓名（Redis缓存键）
     * @param personId 人员ID（市局系统ID）
     * @return 非null的位置信息列表（可能为空）
     */
    public ReceiveEmployeePosition personPosition(String userName, String personId) {
        // 1. Redis缓存查询（避免null，统一返回空列表）
        List<Object> cacheItems = redisCache.getCacheList(userName);
        cacheItems = Optional.ofNullable(cacheItems).orElse(Collections.emptyList());

        // 只取前十条数据
        if (cacheItems.size() > 10) {
            cacheItems = new ArrayList<>(cacheItems.subList(0, 10));
        }
        log.info("查询人员位置：userName={}，从Redis缓存获取到 {} 条原始数据（已截取前10条）", userName, cacheItems.size());

        if (cacheItems.isEmpty()) {
            return null;
        }

        List<ReceiveEmployeePosition> positionList = new ArrayList<>();
        long fiveMinutesAgo = System.currentTimeMillis() - 5 * 60 * 1000; // 5分钟前时间戳

        for (Object item : cacheItems) {
            // 2. 缓存数据校验
            if (!(item instanceof String)) {
                log.warn("查询人员位置：缓存数据非字符串类型（userName={}，数据={}）", userName, item);
                continue;
            }

            // 3. JSON解析（捕获格式错误）
            cn.hutool.json.JSONObject jsonObject;
            try {
                jsonObject = new cn.hutool.json.JSONObject((String) item);
            } catch (Exception e) {
                log.error("查询人员位置：JSON解析失败（userName={}，原始数据={}）", userName, item, e);
                continue;
            }

            cn.hutool.json.JSONObject params = jsonObject.getJSONObject("params");
            if (params == null) {
                log.warn("查询人员位置：数据缺少params节点（userName={}，JSON数据={}）", userName, jsonObject);
                continue;
            }

            // 4. 定位时间戳处理（必选字段，缺失则跳过）
            Long locationTime = params.getLong("locationTime");
            if (locationTime == null) {
                locationTime = params.getLong("inDoor"); // 尝试备用字段
            }
            if (locationTime == null) {
                log.warn("查询人员位置：locationTime和inDoor字段均缺失（userName={}，params={}）", userName, params);
                continue;
            }

            // 5. 过滤5分钟前的旧数据（时间戳比较）
            if (locationTime < fiveMinutesAgo) {
                log.debug("查询人员位置：数据已过期（{}分钟前，userName={}）", (fiveMinutesAgo - locationTime)/60000, userName);
                continue; // 注意：原代码用break，会中断循环，此处改为continue保留后续可能的有效数据
            }

            // 6. 构建位置信息对象（提取坐标、楼层等关键信息）
            ReceiveEmployeePosition position = buildPositionVO(params, locationTime, personId);
            if (position != null) {
                return position;
            }
        }

        return null;
    }




    // 新增：构建位置信息VO（提取重复逻辑，减少冗余）
    private ReceiveEmployeePosition buildPositionVO(cn.hutool.json.JSONObject params, long locationTime, String personId) {
        try {
            ReceiveEmployeePosition vo = new ReceiveEmployeePosition();
            vo.setId(UUID.randomUUID().toString());
            vo.setPersonId(personId);
            vo.setCompanyCode(GlobalConstants.COMPANY_CODE);

            // 时间格式化（复用同一时间戳）
            Date locationDate = new Date(locationTime);
            String timeStamp = DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss", locationDate);
            vo.setTimeStamp(timeStamp);
            vo.setCreateDate(timeStamp);
            vo.setUpdateDate(timeStamp);

            // 坐标解析（必选字段，非数字则抛出异常）
            float longitude = params.getFloat("longitude");
            float latitude = params.getFloat("latitude");
            vo.setLongitude(String.format("%.6f", longitude));
            vo.setLatitude(String.format("%.6f", latitude));

            // 可选字段（楼层、海拔、速度，缺失时设默认值）
            vo.setFloorNo(Optional.ofNullable(params.getInt("floor")).orElse(1).toString());
            vo.setAltitude(formatNumeric(params.getDouble("altitude"), "%.2f", ""));
            vo.setSpeed(formatNumeric(params.getDouble("speed"), "%.2f", ""));

            // 固定字段
            vo.setDeleted("0");
            vo.setCreateBy("系统管理员");
            vo.setUpdateBy("系统管理员");
            vo.setDirection("");

            return vo;
        } catch (Exception e) {
            log.error("构建位置VO失败（params={}，locationTime={}）", params, locationTime, e);
            return null;
        }
    }



    // 新增：数字格式化辅助方法（处理null和非数字场景）
    private String formatNumeric(Number number, String format, String defaultValue) {
        return Optional.ofNullable(number)
                .map(num -> String.format(format, num))
                .orElse(defaultValue);
    }


}
